<?php

declare(strict_types=1);

use think\Exception;
use think\facade\Cache;
use think\facade\Config;
use think\facade\Db;
use think\facade\Event;
use think\facade\Route;
use think\helper\Str;
use think\Request;
use think\route\Url;

const DS = DIRECTORY_SEPARATOR;
// 插件类库自动载入
spl_autoload_register(function ($class) {
    $class     = ltrim($class, '\\');
    $namespace = 'addons';

    if (str_starts_with($class, $namespace)) {
        $dir   = app()->getRootPath();
        $class = substr($class, strlen($namespace));
        $path  = '';

        if (($pos = strripos($class, '\\')) !== false) {
            $path  = str_replace('\\', '/', substr($class, 0, $pos)) . '/';
            $class = substr($class, $pos + 1);
        }
        $path .= str_replace('_', '/', $class) . '.php';
        $dir  .= $namespace . $path;

        if (file_exists($dir)) {
            include $dir;

            return true;
        }

        return false;
    }

    return false;
});

if (!function_exists('hook')) {
    /**
     * 处理插件钩子
     *
     * @param string                    $event  钩子名称
     * @param array|\think\Request|null $params 传入参数
     * @param bool                      $once   是否只返回一个结果
     *
     * @return mixed
     */
    function hook(string $event, array|Request $params = null, bool $once = false): mixed
    {
        return Event::trigger($event, $params, $once);
    }
}

if (!function_exists('getAddonsInfo')) {
    /**
     * 读取插件的基础信息
     *
     * @param string $name 插件名
     *
     * @return array
     */
    function getAddonsInfo(string $name): array
    {
        $addon = getAddonsInstance($name);

        if (!$addon) {
            return [];
        }

        return $addon->getInfo();
    }
}

if (!function_exists('setAddonsInfo')) {
    /**
     * 设置基础配置信息
     *
     * @param string $name  插件名
     * @param array  $array 配置数据
     *
     * @return bool
     * @throws Exception
     */
    function setAddonsInfo(string $name, array $array): bool
    {
        $addons_path = app()->addons->getAddonsPath();
        // 插件列表
        $file  = $addons_path . $name . DS . 'plugin.ini';
        $addon = getAddonsInstance($name);
        $array = $addon->setInfo($name, $array);
        $array['status'] ? $addon->enabled() : $addon->disabled();

        if (!isset($array['name']) || !isset($array['title']) || !isset($array['version'])) {
            throw new Exception(lang('failed to write plugin config'));
        }
        $res = [];

        foreach ($array as $key => $val) {
            if (in_array($key, ['wechat', 'require'])) {
                continue;
            }

            if (is_array($val)) {
                $res[] = "[$key]";

                foreach ($val as $k => $v) {
                    $res[] = "$k = " . $v;
                }
            } else {
                $res[] = "$key = " . $val;
            }
        }

        if ($handle = fopen($file, 'w')) {
            fwrite($handle, implode("\n", $res) . "\n");
            fclose($handle);
            //清空当前配置缓存
            Config::set($array, "addon_{$name}_info");
            Cache::delete('addonslist');
        } else {
            throw new Exception(lang('file does not have write permission'));
        }

        return true;
    }
}

if (!function_exists('getAddonsInstance')) {
    /**
     * 获取插件的单例
     *
     * @param string $name 插件名
     *
     * @return mixed|null
     */
    function getAddonsInstance(string $name): mixed
    {
        static $_addons = [];

        if (isset($_addons[$name])) {
            return $_addons[$name];
        }
        $class = getAddonsClass($name);

        if (class_exists($class)) {
            $_addons[$name] = new $class(app());

            return $_addons[$name];
        }

        return null;
    }
}

if (!function_exists('getAddonsClass')) {
    /**
     * 获取插件类的类名
     *
     * @param string      $name  插件名
     * @param string      $type  返回命名空间类型
     * @param string|null $class 当前类名
     *
     * @return string
     */
    function getAddonsClass(string $name, string $type = 'hook', string $class = null): string
    {
        $name = trim($name);
        // 处理多级控制器情况
        if (!is_null($class) && strpos($class, ' . ')) {
            $class = explode(' . ', $class);

            $class[count($class) - 1] = Str::studly(end($class));
            $class                    = implode('\\', $class);
        } else {
            $class = Str::studly(is_null($class) ? $name : $class);
        }

        if ($type == 'controller') {
            $namespace = '\\addons\\' . $name . '\\controller\\' . $class;
        } else {
            $namespace = '\\addons\\' . $name . '\\Plugin';
        }

        return class_exists($namespace) ? $namespace : '';
    }
}

if (!function_exists('getAddonsConfig')) {
    /**
     * 获取插件的配置
     *
     * @param string $name 插件名
     *
     * @return array
     */
    function getAddonsConfig(string $name): array
    {
        $addon = getAddonsInstance($name);

        if (!$addon) {
            return [];
        }

        return $addon->getConfig();
    }
}

if (!function_exists('setAddonsConfig')) {
    /**
     * @throws \think\Exception
     */
    function setAddonsConfig(string $name, array $array): bool
    {
        $file = app()->addons->getAddonsPath() . $name . DS . 'config.php';

        if ($handle = fopen($file, 'w')) {
            fwrite($handle, "<?php\n\n" . 'return ' . var_export($array, true) . ';');
            fclose($handle);
        } else {
            throw new Exception(lang('file does not have write permission'));
        }

        return true;
    }
}

if (!function_exists('addonsUrl')) {
    /**
     * 插件显示内容里生成访问插件的url
     *
     * @param string      $url
     * @param array       $param
     * @param bool|string $suffix 生成的URL后缀
     * @param bool|string $domain 域名
     *
     * @return Url
     */
    function addonsUrl(
        string $url = '',
        array $param = [],
        bool|string $suffix = true,
        bool|string $domain = false
    ): Url {
        $request = app('request');

        if (empty($url)) {
            // 生成 url 模板变量
            $addons     = $request->addon;
            $controller = $request->controller();
            $controller = str_replace(' / ', ' . ', $controller);
            $action     = $request->action();
        } else {
            $url = Str::studly($url);
            $url = parse_url($url);

            if (isset($url['scheme'])) {
                $addons     = Str::lower($url['scheme']);
                $controller = $url['host'];
                $action     = trim($url['path'], '/');
            } else {
                $route      = explode(' / ', $url['path']);
                $addons     = $request->addon;
                $action     = array_pop($route);
                $controller = array_pop($route) ?: $request->controller();
            }
            $controller = Str::snake((string)$controller);

            /* 解析URL带的参数 */
            if (isset($url['query'])) {
                parse_str($url['query'], $query);
                $param = array_merge($query, $param);
            }
        }

        return Route::buildUrl("@addons/$addons/$controller/$action", $param)->suffix($suffix)->domain($domain);
    }
}

if (!function_exists('getAddonsList')) {
    /**
     * 获得插件列表
     *
     * @return array
     */
    function getAddonsList(): array
    {
        if (!Cache::get('addonslist')) {
            // 定义要遍历的目录
            $directory = new DirectoryIterator(app()->addons->getAddonsPath());
            $list      = [];
            // 遍历目录并打印信息
            foreach ($directory as $fileinfo) {
                if ($fileinfo->isDot()) {
                    continue;
                }
                if ($fileinfo->isDir()) {
                    if (!is_file($fileinfo->getPathname() . DS . 'Plugin.php')) {
                        continue;
                    }
                    $info = getAddonsInfo($fileinfo->getBasename());
                    if (!isset($info['name'])) {
                        continue;
                    }
                    $info['install']                = $info['install'] ?? 0;
                    $list[$fileinfo->getBasename()] = $info;
                }
            }
            Cache::set('addonslist', $list);
        } else {
            $list = Cache::get('addonslist');
        }

        return $list;
    }
}

if (!function_exists('getAddonsMenu')) {
    /**
     * 获取插件菜单
     *
     * @param string $name 插件名称
     *
     * @return array
     */
    function getAddonsMenu(string $name): array
    {
        $menu = app()->addons->getAddonsPath() . $name . DS . 'menu.php';

        if (file_exists($menu)) {
            return include_once $menu;
        }

        return [];
    }
}
if (!function_exists('setAddonConfig')) {
    /**
     * 设置插件钩子
     *
     * @param        $config
     * @param        $methods
     * @param        $base
     * @param string $name 插件名称
     *
     * @return void
     */
    function setAddonConfig(&$config, $methods, $base, string $name): void
    {
        $hooks = array_diff($methods, $base);
        // 循环将钩子方法写入配置中
        foreach ($hooks as $hook) {
            if (!isset($config['hooks'][$hook])) {
                $config['hooks'][$hook] = [];
            }
            // 兼容手动配置项
            if (is_string($config['hooks'][$hook])) {
                $config['hooks'][$hook] = explode(',', $config['hooks'][$hook]);
            }

            if (!in_array($name, $config['hooks'][$hook])) {
                $config['hooks'][$hook][] = $name;
            }
        }
    }
}
if (!function_exists('getAddonsAutoloadConfig')) {
    /**
     * 获得插件自动加载的配置
     *
     * @param bool $chunk 是否清除手动配置的钩子
     *
     * @return array
     */
    function getAddonsAutoloadConfig(bool $chunk = false): array
    {
        // 读取addons的配置
        $config = (array)config('addons');

        if ($chunk) {
            // 清空手动配置的钩子
            $config['hooks'] = [];
        }
        $route = [];
        // 读取插件目录及钩子列表
        $base = get_class_methods('\\wym\\addons\\Addons');
        $base = array_merge($base, ['init', 'initialize', 'install', 'uninstall', 'enabled', 'disabled']);

        $url_domain_deploy = config('route.url_domain_deploy');
        $addons            = getAddonsList();
        $domain            = [];

        foreach ($addons as $name => $addon) {
            if (!$addon['install']) {
                continue;
            }

            if (!$addon['status']) {
                continue;
            }
            // 读取出所有公共方法
            $methods = get_class_methods('\\addons\\' . $name . '\\' . 'Plugin');
            setAddonConfig($config, $methods, $base, $name);
            $conf = getAddonsConfig($addon['name']);

            if ($conf) {
                $conf['rewrite'] = isset($conf['rewrite']) && is_array($conf['rewrite']) ? $conf['rewrite'] : [];
                $rule            = $conf['rewrite'] ? $conf['rewrite']['value'] : [];

                if ($url_domain_deploy && isset($conf['domain']) && $conf['domain']) {
                    $domain[] = [
                        'addons' => $addon['name'],
                        'domain' => $conf['domain']['value'],
                        'rule'   => $rule,
                    ];
                } else {
                    $route = array_merge($route, $rule);
                }
            }
        }
        $config['route'] = $route;
        $config['route'] = array_merge($config['route'], $domain);

        return $config;
    }
}

if (!function_exists('refreshAddons')) {
    /**
     * 刷新插件缓存文件
     *
     * @return bool
     * @throws Exception
     */
    function refreshAddons(): bool
    {
        $file = config_path() . 'addons.php';

        $config = getAddonsAutoloadConfig(true);

        if (!isset($config['autoload']) || !$config['autoload']) {
            return false;
        }

        if ($handle = fopen($file, 'w')) {
            fwrite($handle, "<?php\n\n" . 'return ' . var_export($config, true) . ';');
            fclose($handle);
        } else {
            throw new Exception(lang('file does not have write permission'));
        }

        return true;
    }
}

if (!function_exists('isReallyWritable')) {
    /**
     * 判断文件或目录是否有写的权限
     *
     * @param mixed $file
     *
     * @return bool
     */
    function isReallyWritable(mixed $file): bool
    {
        if (DIRECTORY_SEPARATOR == '/' and !@ ini_get('safe_mode')) {
            return is_writable($file);
        }

        if (!is_file($file) || ($fp = @fopen($file, 'r+')) === false) {
            return false;
        }
        fclose($fp);

        return true;
    }
}

if (!function_exists('importSql')) {
    /**
     * 导入SQL
     *
     * @param string $name 插件名称
     * @param string $prefix
     *
     * @return bool
     * @throws \think\Exception
     */
    function importSql(string $name, string $prefix = '__PREFIX__'): bool
    {
        $sqlFile = app()->addons->getAddonsPath() . $name . DS . 'install.sql';

        return extracted($sqlFile, $prefix);
    }

    if (!function_exists('extracted')) {
        /**
         * @param string $sqlFile
         * @param string $prefix
         *
         * @return true
         * @throws \think\Exception
         */
        function extracted(string $sqlFile, string $prefix = '__PREFIX__'): bool
        {
            if (is_file($sqlFile)) {
                $gz  = fopen($sqlFile, 'r');
                $sql = '';

                while (1) {
                    $sql .= fgets($gz);

                    if (preg_match('/.*;$/', trim($sql))) {
                        $sql = preg_replace('/(\/\*(\s|.)*?\*\/);/', '', $sql);
                        $sql = str_replace($prefix, config('database.connections.mysql.prefix'), $sql);

                        if (str_contains($sql, 'CREATE TABLE') || str_contains($sql, 'INSERT INTO') || str_contains(
                                $sql,
                                'ALTER TABLE'
                            ) || str_contains($sql, 'DROP TABLE')) {
                            try {
                                Db::execute($sql);
                            } catch (\Exception $e) {
                                throw new Exception($e->getMessage());
                            }
                        }
                        $sql = '';
                    }

                    if (feof($gz)) {
                        break;
                    }
                }
            }

            return true;
        }
    }
}

if (!function_exists('upgrade')) {
    /**
     * @throws \think\Exception
     */
    function upgrade(string $name, string $prefix = '__PREFIX__'): bool
    {
        $sqlFile = app()->addons->getAddonsPath() . $name . DS . 'upgrade.sql';

        return extracted($sqlFile, $prefix);
    }
}
if (!function_exists('uninstallSql')) {
    /**
     * 卸载SQL
     *
     * @param        $name
     * @param string $prefix
     *
     * @return bool
     * @throws \think\Exception
     */
    function uninstallSql($name, string $prefix = '__PREFIX__'): bool
    {
        $sqlFile = app()->addons->getAddonsPath() . $name . DS . 'uninstall.sql';

        if (is_file($sqlFile)) {
            $sql = file_get_contents($sqlFile);
            $sql = str_replace($prefix, config('database.connections.mysql.prefix'), $sql);
            $sql = array_filter(explode("\r\n", $sql));

            foreach ($sql as $v) {
                try {
                    Db::execute($v);
                } catch (\Exception $e) {
                    throw new Exception($e->getMessage());
                }
            }
        }

        return true;
    }
}
